package s

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"reflect"
	"strconv"
	"unsafe"
)

type Number interface {
	int32 | float32 | float64 | int64
}

func TryAsNumber[num Number](a string) (num, error) {
	e, err := strconv.ParseFloat(a, 32)
	if err != nil {
		e2, err := strconv.ParseInt(a, 10, 32)
		if err != nil {
			return -1, err
		}
		return num(e2), nil
	}
	return num(e), nil
}

func PrintlnStrcut(obj any) {

	v := reflect.ValueOf(obj)
	EName := reflect.TypeOf(obj).Elem().Name()
	// 解析字段
	fInt := 0
	Str("[" + EName + "]").ANSIStrike().Println()
	for i := 0; i < v.Elem().NumField(); i++ {

		// 取tag
		field := v.Elem().Type().Field(i)
		tag := field.Tag

		// 解析label 和 default
		label := Str("")
		if Str(tag).Len() > 0 {
			label = Str("#  (") + Str(tag) + Str(")")
		}
		tp := v.Elem().Field(i).Type().Name()
		// fmt.Println(tp)
		if tp == "string" {
			Str("{name} = {value} {label}").Format(Dict[Str, any]{
				"name":  Str(field.Name).ANSIYellow(),
				"value": Str(v.Elem().Field(i).String()).ANSIGreen(),
				"label": label.ANSIUnderline(),
			}).Println()
		} else if tp == "s.Str" {
			Str("{name} = {value} {label}").Format(Dict[Str, any]{
				"name":  Str(field.Name).ANSIYellow(),
				"value": Str(v.Elem().Field(i).String()).ANSIGreen(),
				"label": label.ANSIUnderline(),
			}).Println()

		} else if tp == "Str" {
			Str("{name} = {value} {label}").Format(Dict[Str, any]{
				"name":  Str(field.Name).ANSIYellow(),
				"value": Str(v.Elem().Field(i).String()).ANSIGreen(),
				"label": label.ANSIUnderline(),
			}).Println()

		} else if tp == "int" {
			Str("{name} = {value} {label}").Format(Dict[Str, any]{
				"name":  Str(field.Name).ANSIYellow(),
				"value": Sprintf("%d", v.Elem().Field(i).Int()).ANSIGreen(),
				"label": label.ANSIUnderline(),
			}).Println()

		} else if tp == "bool" {
			Str("{name} = {value} {label}").Format(Dict[Str, any]{
				"name":  Str(field.Name).ANSIYellow(),
				"value": Sprintf("%v", v.Elem().Field(i).Bool()).ANSIGreen(),
				"label": label.ANSIUnderline(),
			}).Println()
		}
	}
	if fInt > 0 {
		Args()
	}
}

func FromStructToINIStr(obj any) Str {
	v := reflect.ValueOf(obj)
	EName := reflect.TypeOf(obj).Elem().Name()
	// 解析字段
	end := Str("")
	fmt.Println("[" + EName + "]")
	for i := 0; i < v.Elem().NumField(); i++ {

		// 取tag
		field := v.Elem().Type().Field(i)
		tag := field.Tag

		// 解析label 和 default
		label := Str("")
		if Str(tag).Len() > 0 {
			label = Str("#  (") + Str(tag) + Str(")")
		}
		end.A(Str("{name} = {value} {label}").Format(Dict[Str, any]{
			"name":  Str(field.Name),
			"value": Str(v.Elem().Field(i).String()),
			"label": label,
		}))
	}
	return end
}

func ArgsFromINIStruct(obj interface{}) {

	v := reflect.ValueOf(obj)

	// 解析字段
	fInt := 0
	for i := 0; i < v.Elem().NumField(); i++ {

		// 取tag
		field := v.Elem().Type().Field(i)
		tag := field.Tag

		// 解析label 和 default
		label := tag.Get("ini")
		if label == "" {
			continue
		}
		defaultValue := tag.Get("default")
		if defaultValue == "" {
			defaultValue = tag.Get("def")
		}
		// fmt.Println(defaultValue)

		desc := tag.Get("desc")
		if desc == "" {
			desc = "val of " + label + " default:" + defaultValue
		}
		// c := label.(K)
		// fmt.Println(v.Elem().Field(i).Type().String())
		switch v.Elem().Field(i).Type().String() {
		case "s.Str":
			// fmt.Println("oo")
			flag.StringVar((*string)(unsafe.Pointer(v.Elem().Field(i).Addr().Pointer())),
				label,
				defaultValue,
				desc)
			fInt += 1
		case "Str":
			// fmt.Println("oo2")
			flag.StringVar((*string)(unsafe.Pointer(v.Elem().Field(i).Addr().Pointer())),
				label,
				defaultValue,
				desc)
			fInt += 1

		case "string":
			flag.StringVar((*string)(unsafe.Pointer(v.Elem().Field(i).Addr().Pointer())),
				label,
				defaultValue,
				desc)
			fInt += 1
		case "int":
			defaultValueInt, err := strconv.Atoi(defaultValue)
			if err != nil {
				defaultValueInt = 0
			}
			flag.IntVar((*int)(unsafe.Pointer(v.Elem().Field(i).Addr().Pointer())),
				label,
				defaultValueInt,
				desc)
			fInt += 1
		case "bool":
			defaultValuebool, err := strconv.ParseBool(defaultValue)
			if err != nil {
				defaultValuebool = false
			}
			flag.BoolVar((*bool)(unsafe.Pointer(v.Elem().Field(i).Addr().Pointer())),
				label,
				defaultValuebool,
				desc)
			fInt += 1
		}
	}
	if fInt > 0 {
		// fmt.Println()
		Args()
	} else {
		fmt.Println("No")
	}

}

func Args() {
	// flag.CommandLine.

	oldArgs := Dict[Str, any]{}
	flag.CommandLine.VisitAll(func(f *flag.Flag) {
		// fmt.Println(f.Name, f.DefValue)
		oldArgs[Str(f.Name)] = f.DefValue
	})
	initConfig := false
	flag.BoolVar(&initConfig, "ini", false, "generate ini conf from args flag parser!")
	loadPath := ""
	flag.StringVar(&loadPath, "load", "", "load ini file to set pargs default ")
	flag.Parse()
	if loadPath != "" {
		if f, err := os.Stat(loadPath); err == nil && !f.IsDir() {
			buf, err := ioutil.ReadFile(loadPath)
			if err != nil {
				log.Fatal("load ini file err:", err)
			}
			content := Bytes(buf).Decode()
			// mode := false

			oldArgslst := []string{}
			c := false
			for _, a := range os.Args {
				if c {

					c = false
					continue
				}
				if a == "-load" {
					c = true
					continue
				}
				oldArgslst = append(oldArgslst, a)
			}
			exec := os.Args[0]
			args := []string{}
			Dd := Dict[Str, any]{}
			content.Split("\n").Each(func(i int, item Str) {
				if item.Has("=") {
					key, val := item.Cut2("=")
					Dd[key] = val.Strip()
					// fmt.Println("-", key, val)
					if val != "" {
						args = append(args, "-"+key.String(), val.String())

					}

				}
			})

			// Dd.Each(func(k Str, v any) {
			// 	k.A(v).Println("---")
			// })
			// fmt.Println("0", )
			flag.CommandLine.VisitAll(func(f *flag.Flag) {
				// fmt.Println(f.Name, Dd.Keys().Each(func(i int, item Str) {
				// 	fmt.Println("byte:", item.Encode())
				// }))
				if vltmp, ok := Dd[Str(f.Name).Strip()]; ok {
					if vstr, ok := vltmp.(string); ok {
						f.DefValue = vstr
						f.Value.Set(vstr)
					} else if vstr, ok := vltmp.(Str); ok {
						f.DefValue = vstr.String()
						f.Value.Set(vstr.String())
					} else {
						f.DefValue = fmt.Sprint(vstr)
						f.Value.Set(fmt.Sprint(vstr))
					} // fmt.Println("{}", f.Name, vltmp)

				}
				// fmt.Println(f.Name, f.DefValue)

			})
			os.Args = append([]string{exec}, args...)
			// fmt.Println("test:", os.Args)
			// List[string](os.Args).Each(func(i int, item string) {
			// 	Str(item).Strip().Println("|")
			// })

		} else {
			Str("err").Println()
		}
	} else if initConfig {
		o, _ := oldArgs.AsStr("{key} = {val}", "\n", "[{key}]\n")
		fmt.Println("[args]\n" + o)
		os.Exit(0)
	}
}

func FromIni(content Str) Dict[Str, any] {
	sec := Str("")
	d := make(Dict[Str, any])
	content.Split("\n").Each(func(i int, item Str) {
		names := item.ReFindAll(`\[\w+\]`)
		if names.Len() == 1 {
			sec = names[0].Remove("[]")
			d[sec] = make(Dict[Str, any])
			return
		}

		if sec != "" {
			if item.Has("=") {
				key, val := item.Cut2("=")
				d[sec].(Dict[Str, any])[key] = val
			}
		}
	})
	return d
}
